from driver import UART
from driver import ADC
import utime as time
import uos
import ujson
import serial_base
import ubinascii
import ustruct
import power
import app_relay
import app_config
# import sleep_mode


class SerialDrver(object):
    def __init__(self,port="rs232",buffer_size=50):
        self.uart_rs232 = UART()
        self.uart_rs232.open(port)
        self.uart_rs232.on(self.uart_start_recieve)
        #电源电压
        self.adc1Obj = ADC()
        self.adc1Obj.open('adc1')

        #标志位
        self.receiveing = False
        self.collect_flag = True      # 切换自动采集和软件连接的标志位
        self.recieve_data = None
        #回调函数
        self.sensors_hander = None  # 读取传感器 回调
        self.RTCcalback = None  # RTC 回调
        # TF卡变量
        self.tf_file_path = '/sdcard0/'
        self.TfObj  = None
    
        self.send_index = 0  # tf发送索引
        self.total_lines = 0  # tf总行数
        self.frame_type = {
        'start_frame': 0x01,
        'data_frame': 0x02,
        'end_frame': 0x03
         }
        
        self.interval = 0  # 采集时间间隔
        self.interval_init()
        # 日期时间变量
        self.time_list = {'year':2024,'month':12,'day':1,'hour':0,
                       'minute':0,'second':0,'week':1}  # 时间列表      
        # 数据帧变量
        self.frames =[]            # 接收数据帧
        self.cmd_head = ''         # 帧头
        self.cmd_type = ''         # 命令类型(UPDATE_CONTROL)
        self.ctrl_msg = ''         # CtrlMsgType-指示消息的类型
        self.first_id = ''        # 产生消息的第一识别ID
        self.second_id = ''       # 产生消息的第二识别ID
        self.frame_type_id = ''     # 控件类型
        self.param = ''            # 可变长度参数
        self.cmd_tail = ''         # 帧尾

        # self.display_run()      # 这里不开启UI更新线程


    def interval_init(self):
        self.interval = app_config.read_interval_from_config()
       
          
    def ReciveData(self,data):
        """
        brief: 获取串口屏数据帧
        return: 返回最后一帧数据
        """
        data_head = "ee"
        data_end = "fffcffff"
        hand_index = 0
        size = len(data)
        # 二进制字节串转16进制再解码为字符串
        datastr = ubinascii.hexlify(data).decode('utf-8')
        # print(datastr)
        # print(size)
        # 寻找所有的帧数据
        while size > 0:
            # 寻找帧头
            hand_index = datastr.find(data_head,hand_index)
            if hand_index == -1:
                break
            # 寻找帧尾
            end_index = datastr.find(data_end,hand_index)
            if end_index == -1:
                break
            end_index += len(data_end)
            frame  = datastr[hand_index:end_index]
            self.frames.append(frame)
            hand_index = end_index 
            # print(hand_index)
        if self.frames:
            # 获取最后一个帧
            last_frame = self.frames[-1]
            self.frames= []                 # 清空当前阵列表以准备下一次读取
            # print(last_frame)
            return last_frame
        else:
            return None


    def uart_start_recieve(self,recdata):  
        
        data = self.ReciveData(recdata)    # 获取串口屏数据
        if data:
            # print('recive data:',data)
            self.pare_cmd_buffer(data)
            self.ProcessMessage()  

    def pare_cmd_buffer(self,dataframe):
        """
        brief: 解析数据帧
        """
        self.cmd_head = dataframe[:2]              # 帧头   半个字节
        self.cmd_type = dataframe[2:4]             # 命令类型(UPDATE_CONTROL)如，0xB1
        self.ctrl_msg = dataframe[4:6]             # 指示消息的类型,如，0x11发送mcu或者0x10接收mcu信息
        self.first_id = dataframe[6:10]           # 产生消息的第一识别ID
        self.second_id = dataframe[10:14]           # 产生消息的第二识别ID  1bytes
        self.frame_type_id = dataframe[14:16]         # 控件类型 如，0x10为按钮
        self.param = dataframe[16:-8]                # 可变长度参数
        self.cmd_tail = dataframe[-8:]               # 帧尾

      # 信息处理流程  
    def ProcessMessage(self):
        frame_type_id = {
            '10' : self.NotifyButton,  # 按钮
            '11' : self.NotifyText,    # 文本  
        }
        # print(self.cmd_type)
        if self.cmd_type == "b1":
            handler = frame_type_id.get(self.frame_type_id)
            if handler:
                handler(self.first_id,self.second_id,self.param)
            else:
                print("Unknown control type")
        elif self.cmd_type == 'f7':
              self.set_rtc_message()
        elif self.cmd_type == 'dd':
            #   sleep_mode.voltage_3v3(1)  # 拉高3.3V电源
            #   sleep_mode.sys_sleep(0)     # 退出休眠状态
              self.read_rtc_message()
              self.send_text_value(0,1,str(self.interval)) 
            #   self.collect_flag = False            
              app_relay.relay_ctl(1,1)
        elif self.cmd_type == 'e7':  # 断开上位机的连接
            #  sleep_mode.voltage_3v3(0)  # 拉低3.3V电源
            #  sleep_mode.sys_sleep(1)
            #  self.collect_flag = True
             app_relay.relay_ctl(1,0)
        elif self.cmd_type == 'e8':    # 读取TF卡文件信息
            self.read_tf_file()      # 读取tf卡文件

        elif self.cmd_type == 'e9':   # 读取实时数据
            self.updata_table_data()
             
        else:
            return

    # 读取TF卡文件名称
    def read_tf_file(self):
        files = uos.listdir(self.tf_file_path) 
        for file in files:
            if not file.lower().endswith('.csv'):
                continue
            # 获取文件大小
            file_info = uos.stat(self.tf_file_path + file)
            if file_info[6] <= 0:
                continue
            self.uart_rs232.write(file.encode('utf-8'))
            size = ustruct.pack('>I',file_info[6])
            self.uart_rs232.write(size)
        
                               
    def set_rtc_message(self):
        '''
        brief: 从显示屏设置的时间中获取RTC时间
        '''
        get_rtc = (self.ctrl_msg + self.first_id + self.second_id 
        + self.frame_type_id +self.param )
        year = int(get_rtc[0:2])  
        month = int(get_rtc[2:4])  
        week = int(get_rtc[4:6])   
        day = int(get_rtc[6:8])     
        hour = int(get_rtc[8:10])   
        minute = int(get_rtc[10:12]) 
        second = int(get_rtc[12:14])
        # 设置校准标志位
        if self.RTCcalback:
            self.RTCcalback(year=(2000+year),month=month,day=day,hour=hour,minute=minute,second=second)


    def read_rtc_message(self):
        '''
        brief: 读取RTC时间
        '''
        if self.RTCcalback:
            try:
                # 获取当前RTC时间
                data_time = self.RTCcalback()
                if data_time is not None and isinstance(data_time, str):
                    # print('time',data_time)
                    self.send_text_value(0,0,data_time)
                   
            except Exception as e:
                # self.send_text_value(0,0,'RTCError')
                print("Error getting RTC time:", e)
        
        
    def NotifyButton(self,first_id, second_id,state):
        '''
          brief  按钮控件通知  
          details  当按钮状态改变，执行此函数
          param first_id 第一识别ID
          param second_id 第二识别ID
          param state 按钮状态:0 弹起,1按下
        '''
        first_id = first_id[-2:]
        second_id = second_id[-2:]
        # print(first_id,second_id)
        if first_id == '00' and second_id == '1f':
            # self.ReadRTC() 
            # self.collect_flag = True  
            print("start logging")
        if first_id == '00' and second_id == '20':
            # self.collect_flag = False 
            print("stop logging")
            
        if first_id == '00' and second_id == '16':
            self.ReadRTC() 

    def NotifyText(self,first_id, second_id,text):
        '''
          brief  文本控件通知  
          details  当文本通过屏幕按键更新，执行此函数
          param first_id 第一识别ID
          param second_id 第二识别ID
          param  text     文本内容
        '''
        first_id = first_id[-2:]
        second_id = second_id[-2:]
        if first_id == '00' and second_id == '12':
            try:
                ascii_string = ubinascii.unhexlify(text).decode('utf-8').split('\0')   # 将16进制转字节串再转字符串
                # print("ascii_string:",ascii_string) #ascii_string: ['001000', '']
                interval = ascii_string[0]
                # 保存到文件
                if app_config.write_interval_to_config(int(interval)):
                    self.interval = int(interval)
                else:
                    self.interval = 300
            except Exception as e:
                print("Failed to update interval:",e)


        if first_id == '00' and second_id == '13':
             ascii_string = ubinascii.unhexlify(text).decode('utf-8').split('\0')
             tf_file_str= ascii_string[0]
             filename = self.tf_file_path + tf_file_str
             self.send_data_file(filename)
            
        if first_id == '00' and second_id == '14':
            ascii_string = ubinascii.unhexlify(text).decode('utf-8').split('\0')
            del_tf_file_str= ascii_string[0]
            try:
                uos.remove(self.tf_file_path +del_tf_file_str)
            
            except Exception as e:
                print("Failed to delete file:",e)
                return

        if first_id == '00' and second_id == '15':
            ascii_string = ubinascii.unhexlify(text).decode('utf-8').split('\0')
            del_tf_file_str= ascii_string[0]
            header_obj = ujson.loads(del_tf_file_str)  # 解析 JSON

            parsed = {}
            for k, v in header_obj.items():
                try:
                    idx = int(k)
                except Exception:
                    continue
                parsed[idx] = "" if v is None else str(v).strip()
            # print(parsed)
            app_config.write_headers_to_config(parsed)

    def send_float_value(self,first_id, second_id, floatvalue, precision):
        '''
          brief   设置文本单精度浮点值 
          param  first_id 第一识别ID
          param  second_id 第二识别ID
          param  floatvalue 浮点数值
          param  precision 小数位数，若不够则末尾补零
        '''
        cmd = serial_base.BEGIN_CMD()
        cmd += serial_base.TX_8(0xB1)
        cmd += serial_base.TX_8(0x07)
        cmd += serial_base.TX_16(first_id)
        cmd += serial_base.TX_16(second_id)
        cmd += serial_base.TX_8(0x02)
        cmd += serial_base.TX_8((precision &0x0F) | 0x80 )
        # 将float值转换为字节，并处理大小端 ,这里我们使用大端格式 '>f'，如果需要小端格式，可以使用 '<f'
        float_bytes = ustruct.pack('>f',floatvalue) 
        # 使用大端格式 
        cmd += float_bytes 
        cmd += serial_base.END_CMD()
        # print(cmd)
        self.uart_rs232.write(cmd)
    # 发送字节类型
    def send_int32_value(self,first_id, second_id,value,sign,fill_zero):
        ''' 
            brief  设置文本为整数值
            param  first_id 第一识别ID
            param  second_id 第二识别ID
            param  value 文本数值
            param  sign 0-无符号,1-有符号
            param  fill_zero 位数，不足时左侧补零
        '''
        cmd = serial_base.BEGIN_CMD()
        cmd += serial_base.TX_8(0xB1)
        cmd += serial_base.TX_8(0x07)
        cmd += serial_base.TX_16(first_id)
        cmd += serial_base.TX_16(second_id)
        cmd += serial_base.TX_8(0x01 if sign else 0x00)
        cmd += serial_base.TX_8((fill_zero & 0x0F) | 0x80)
        cmd += serial_base.TX_32(value)
        cmd += serial_base.END_CMD()
        # print(cmd)
        self.uart_rs232.write(cmd)

    def send_text_value(self,first_id,second_id,str):
        '''
          brief     设置文本值
          param  first_id 第一识别ID
          param  second_id 第二识别ID
          param  str 文本值(字符串)
        '''
        cmd = serial_base.BEGIN_CMD()
        cmd += serial_base.TX_8(0xB1)
        cmd += serial_base.TX_8(0x10)
        cmd += serial_base.TX_16(first_id)
        cmd += serial_base.TX_16(second_id)
        cmd += serial_base.SendStrings(str)
        cmd += serial_base.END_CMD()
        self.uart_rs232.write(cmd)

    def send_data_config(self,first_id,second_id,config:dict):
        '''
        brief: 发送配置参数
        param: config 配置参数
        '''
        cmd = serial_base.BEGIN_CMD()
        cmd += serial_base.TX_8(0xB1)
        cmd += serial_base.TX_8(0x10)
        cmd += serial_base.TX_16(first_id)
        cmd += serial_base.TX_16(second_id)
        cmd += serial_base.dict_to_bytes_json(config)
        cmd += serial_base.END_CMD()
        self.uart_rs232.write(cmd)  

    def ReadRTC(self):
        '''
        brief: 读取屏幕的RTC时间,屏幕返回时间数据
        '''
        cmd = serial_base.BEGIN_CMD()
        cmd += serial_base.TX_8((0x2 & 0x0F) | 0x80)
        cmd += serial_base.END_CMD()
        self.uart_rs232.write(cmd)

    def register_calback(self,handler):
        '''
        brief: 注册回调函数
        '''
        if handler:
            self.sensors_hander = handler
        else:
            print("time data handler is None")

    def register_rtc_calback(self,handler):
        '''
        brief: 注册触摸屏RTC回调函数,读取触摸屏时间到系统RTC
        '''
        if handler:
            self.RTCcalback = handler
        else:
            print('RTC RtChandler is None')
        
    def register_tf_calback(self,handler):
        if handler:
            self.TfObj  = handler
        else:
            print('tf calback is None')
            return False
        # 获取当前采集间隔
    def get_interval(self):
        return self.interval

    def get_collect_flag(self):
        return self.collect_flag

    def set_collect_flag(self,flag:bool):
        if flag:
            self.collect_flag = True
        else:
            self.collect_flag = False

   # UI更新
    def updata_table_data(self):
        try:
            if self.sensors_hander and app_relay.get_relay_state(1) == 1:
                self.recieve_data = self.sensors_hander()
                if self.recieve_data :
                    # 发送表头
                    config = app_config.get_config().get('sensors',{})
                    header = config.get('header',{})
                    self.send_data_config(0,4,header) 
                    #发送数据
                    for i in range (len(header)-1):
                        key = "No" + str(i+1)
                        for p in range(len(header.get(str(i+1)))):
                            self.send_float_value(i, p+1, self.recieve_data[key][p]/10, 2)
                    power_votage=float(power.power_votage())
                    self.send_float_value(0,3,power_votage,3) 
                else:
                    # print("no data")
                    return False
            else:
                return  

        except Exception as e:
            print("updata ui error:",e)
            return

    def _create_data_frame(self,frame_type, data=b'', sequence=0):
        """
        创建标准数据帧
        - 帧头 : AA 55 (2字节)
        - frame_type : 1字节 (0x01=开始, 0x02=数据, 0x03=结束)
        - 序列号 : 2字节
        - 数据长度 : 2字节
        - 数据内容 : 变长
        - CRC校验 : 1字节
        - 帧尾 : DF AF (2字节)
        """
        frame = bytearray()
        frame.extend(b'\xAA\x55')  # 固定帧头
        frame.append(frame_type)   # frame_type
        frame.extend(ustruct.pack('<H', sequence))  # 序列号
        frame.extend(ustruct.pack('<H', len(data))) # 数据长度
        frame.extend(data)         # 数据内容
        # CRC校验（简化为异或校验）
        crc = 0
        for byte in frame[2:]:  # 跳过帧头
            crc ^= byte
        frame.append(crc)
        frame.extend(b'\xDF\xAF')  # 帧尾
        return frame
    
    def _bytes_to_hex_str(self, bdata):
        # converts bytes/bytearray to hex string, e.g., b'\xAA\x55' -> "AA55"
        hex_str = ''
        for b in bdata:
            # format each byte to 2 uppercase hex digits
            if b < 16:
                hex_str += '0' + hex(b)[2:].upper()
            else:
                hex_str += hex(b)[2:].upper()
        return hex_str

   

    def send_data_file(self, filename, lines_per_frame=1):
        
        try:
            # 更快的行计数（仍然需要行数做起始帧，保持协议一致）
            total_lines = sum(1 for _ in open(filename, 'r', encoding='utf-8'))
            # 发送起始帧：携带总行数
            start_data = ustruct.pack('<L', total_lines)
            start_frame = self._create_data_frame(self.frame_type['start_frame'], start_data, 0)
            self.uart_rs232.write(start_frame)

            sequence = 1
            # 批量发送：lines_per_frame=1 表示保持原来的“一行一帧”
            with open(filename, 'r', encoding='utf-8') as f:
                batch = []
                for line in f:
                    # 去除行尾换行，保留内容
                    batch.append(line.rstrip('\r\n'))
                    # 达到批量阈值或文件末尾时发送一个数据帧
                    if len(batch) >= lines_per_frame:
                        payload = ("\n".join(batch)).encode('utf-8')
                        data_frame = self._create_data_frame(self.frame_type['data_frame'], payload, sequence)
                        self.uart_rs232.write(data_frame)
                        sequence += 1
                        batch.clear()

                # 发送剩余不足一个批次的行
                if batch:
                    payload = ("\n".join(batch)).encode('utf-8')
                    data_frame = self._create_data_frame(self.frame_type['data_frame'], payload, sequence)
                    self.uart_rs232.write(data_frame)
                    sequence += 1

            # 发送结束帧
            end_frame = self._create_data_frame(self.frame_type['end_frame'], b'', sequence)
            self.uart_rs232.write(end_frame)
        except Exception as e:
            print("[ERROR] Exception during data sending: {}".format(e))


        
    